﻿using System.Linq;
using SLNGP.Core.Genes;
using SLNGP.Core.GeneticPrograms;
using SLNGP.Core.Individuals;
using SLNGP.Core.SecondaryStrategies;

namespace SLNGP.Core.GeneticOpearators
{
    /// <summary>
    /// Samples crossover between 2 Parents.
    /// Uses the first parent in every pair as the chasis, 
    /// and the second parent as the source of the new genes.
    /// In case the new child is invalid an autofix is performed. 
    /// Ignores the last odd parent. 
    /// </summary>
    public class BasicCrossover : GeneticOpearator, ICrossoverGeneticOpearator
    {
        public override void Execute()
        {
            var crossoverGenes = 
                Parent.LastRunResults
                    .LastGeneration
                    .Where(individual => (SelectionMarkers)individual.SelectionMarker == SelectionMarkers.Crossover)
                    .ToArray();
            for (int i = 0; i < crossoverGenes.Count(); i=i+2)
            {
                if (i+1 >= crossoverGenes.Count())
                {
                    Parent.LastRunResults.CurrentGeneration.TryAddUnique(crossoverGenes[i]);
                    break;
                }

                Parent.LastRunResults.CurrentGeneration.Add(crossoverGenes[i]);
                Parent.LastRunResults.CurrentGeneration.Add(crossoverGenes[i + 1]);

                IIndividual crossOverIndividual = GetCrossOver(crossoverGenes, i);

                if (!Parent.LastRunResults.CurrentGeneration.TryAddUnique(crossOverIndividual))
                {
                    Parent.LastRunResults.CurrentGeneration.Add(crossOverIndividual);
                }
                else
                {
                    i = i - 2;
                }

            }
        }

        private IIndividual GetCrossOver(IIndividual[] crossoverGenes, int i)
        {
            IIndividual firstIndividual = crossoverGenes[i].CloneChild();
            IIndividual secondIndividual = crossoverGenes[i + 1];

            int firstIndex = Parent.GetStrategy<IRandomProvider>().Random(0, firstIndividual.AllGenes.Count() - 1);
            IGene firstGene = firstIndividual.AllGenes.ToList()[firstIndex];

            int secondIndex = Parent.GetStrategy<IRandomProvider>().Random(0, secondIndividual.AllGenes.Count() - 1);
            IGene secondGene = secondIndividual.AllGenes.ToList()[secondIndex].ChildClone(null, firstGene.ParentChromosome);


            firstGene.ReplaceWith(secondGene);
                
            if (!firstIndividual.IsValid)
                firstIndividual.Fix();
            return firstIndividual;
        }
    }
}